package ee.ut.math.chess.hadoop;

import java.util.ArrayList;
import java.util.List;

import org.apache.hadoop.io.BytesWritable;
import org.apache.hadoop.io.DoubleWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;

import ee.ut.math.chess.base.model.Move;
import ee.ut.math.chess.base.model.Position;
import ee.ut.math.chess.base.model.chesspiece.Bishop;
import ee.ut.math.chess.base.model.chesspiece.ChessPiece;
import ee.ut.math.chess.base.model.chesspiece.Color;
import ee.ut.math.chess.base.model.chesspiece.King;
import ee.ut.math.chess.base.model.chesspiece.Knight;
import ee.ut.math.chess.base.model.chesspiece.Pawn;
import ee.ut.math.chess.base.model.chesspiece.Queen;
import ee.ut.math.chess.base.model.chesspiece.Rook;
import ee.ut.math.chess.base.model.exception.GameLogicException;
import ee.ut.math.chess.game.model.DummyPersistenceManager;
import ee.ut.math.chess.game.model.Game;
import ee.ut.math.chess.game.model.GameLogic;

@SuppressWarnings("unused")
public class Reduce extends Reducer<Text, BytesWritable, Text, DoubleWritable>{
	private Game game;
	private static String[] dummyMoves = {"a2-a4", "b2-b4","c2-c4","d2-d4","e2-e4","f2-f4","g2-g4","h2-h4"};  
	protected void setup(Context context) throws java.io.IOException ,InterruptedException {
		
	}
	
	protected void reduce(Text key, Iterable<BytesWritable> values, Context context) throws java.io.IOException ,InterruptedException {
		/*//DUMMY IMPLEMENTATION
		for (Text value : values) {
			String dummyMove = dummyMoves[(int)(Math.random()*8)];
			context.write(new Text(dummyMove), value);
		}*/
		BytesWritable gameInBytes = values.iterator().next();
        Game game;
        double evaluateCurrent = 0.0;
        double evaluateOpponent = 0.0;
        DoubleWritable evaluateMove = new DoubleWritable();
        
        try {
            game = HadoopManager.getGameFromBytes(gameInBytes.getBytes());
            Move move = new Move(key.toString());
            ChessPiece currentPiece = game.getBoard().getChessPiece(move.getX1(), move.getY1());
            DummyPersistenceManager manager = new DummyPersistenceManager(game);
            
            game.moveInternal(currentPiece, move);
            if (game.getDepth() == 0){
            	evaluateMove.set(evaluatePlayerScore(game));
            }else{
            	evaluateMove.set(negaMax(game.getDepth(), game));
            }
            manager.rollback();
            
        } catch (ClassNotFoundException e) {
            return;
        } catch (GameLogicException e) {
            return;
        }
        context.write(key, evaluateMove);
	}
	
	/** Method, which returns a positive score value
	 * if the player is black
	 * otherwise a negative value
	 */	
	double evaluatePlayerScore(Game game){
		double score = 0.0;
		//evaluation of players score
        for (int i=0; i < 8; i++) {
            for (int j=0; j <8; j++) {
                ChessPiece chessPiece = game.getBoard().getChessPiece(i, j);
                if (chessPiece != null && chessPiece.getColor() == game.getWaitingForPlayer()) {
                	// Add the piece's value
                     score = score + chessPiece.getValue();
                    // Add the value of legal moves
                    	 score = score + (chessPiece.getLegalMoves(i, j, game, false).size()*0.1);
                     
                    //If under fire
                     Position targetPos = new Position(i, j);
                     Color opponent = chessPiece.getColor().getOpposite();
                   
                     if (GameLogic.positionIsThreatenedBy(targetPos, opponent, game)!= null){
                     	score = score - 5;
                     	if (chessPiece instanceof Bishop ||
                     		chessPiece instanceof Knight){
                     		score = score -10;
                     	}
                     	if (chessPiece instanceof Queen ||
                     		chessPiece instanceof Rook ||
                     		chessPiece instanceof King){
                     		score = score - 15;
                     	}
                     	if (chessPiece instanceof King){
                     		score = score - 20;
                     	}
                     }
                     // IF UNPROTECTED
                     List<Move> protectedMoves = GameLogic.positionIsProtectedBy(targetPos, chessPiece.getColor(), game);
                     Position kingPos = GameLogic.kingsPosition(chessPiece.getColor(), game);
                     Move kingProtect = new Move(kingPos.getX(), kingPos.getY(),i,j);
                     
                     if (protectedMoves == null){
                    	 if (chessPiece instanceof King){
                    		 score = score - 40;
                    	 }else if (chessPiece instanceof Queen ||
                    			   chessPiece instanceof Bishop ||
                    			   chessPiece instanceof Rook){
                    		 score = score - 20;
                    	 }
                     	score = score - 10;
                     }
                     // IF PROTECTED
                     if (protectedMoves!= null){
                    	 if (protectedMoves.size() == 1 && !protectedMoves.contains(kingProtect)){
                    		 score = score + 5;
                    	 }
                        if (protectedMoves.size() >1 && !protectedMoves.contains(kingProtect)){
                           	score = score + 5;
                           	if (protectedMoves.contains(kingProtect) && protectedMoves.size() == 2){
                           		score = score -5;
                           	}
                           }
                      }
                     // IF doubled, blocked or isolated: -0.5
                     if (chessPiece instanceof Pawn){
                    	 // IF BLOCKED
                    	 if (chessPiece.getColor() == Color.WHITE){
                    		 ChessPiece chessPieceBlock = game.getBoard().getChessPiece(i+1, j);
                    		 if (chessPieceBlock != null && chessPieceBlock.getColor() != game.getWaitingForPlayer()){
                				 score = score - 0.5;
                			 }
                    	 }else{
                    		 ChessPiece chessPieceBlock = game.getBoard().getChessPiece(i-1, j);
                    		 if (chessPieceBlock != null && chessPieceBlock.getColor() != game.getWaitingForPlayer()){
                				 score = score - 0.5;
                			 }
                    	 }
                    	 // IF DOUBLED
                    	 for (int u = 0; u < 8; u++){
                    		 if (u != j){
                    			 ChessPiece chessPieceDouble = game.getBoard().getChessPiece(i, u);
                    			 if (chessPieceDouble != null && chessPieceDouble.getColor() == game.getWaitingForPlayer()){
                    				 score = score - 0.5;
                    			 }
                    		 }
                    	 }
                    	 // IF ISOLATED
                		 boolean isolated = true;
                    	 if (j != 0){    
                    		 // Check the left row
                    		 for (int u = i-1; u < i+3 && u < 8; u++){
                    			 ChessPiece chessPieceIso = game.getBoard().getChessPiece(u, j-1);
                    			 if (chessPieceIso != null && chessPieceIso.getColor() == game.getWaitingForPlayer()){
                    				 isolated = false;
                    				 break;
                    			 }
                    		 }
                    	 }
                    	 if (j != 7){
                    		 // Check the right row
                    		 for (int u = i-1; u < i+3 && u < 8; u++){
                    			 ChessPiece chessPieceIso = game.getBoard().getChessPiece(u, j+1);
                    			 if (chessPieceIso != null && chessPieceIso.getColor() == game.getWaitingForPlayer()){
                    				 isolated = false;
                    				 break;
                    			 }
                    		 }
                    	 }
                    	 if (isolated = true){
                			 score = score - 0.5;
                		 }
                     }
                }
            }
        }
        if (game.getWaitingForPlayer() == Color.WHITE)
        	return score*(-1);
        else
        	return score;
	}
	
	//make a recursive evaluation
     double negaMax( int depth, Game game ) {
        if ( depth == 0 ) return evaluatePlayerScore(game);
        double val = evaluatePlayerScore(game);
        double max = Double.NEGATIVE_INFINITY;
        List<Move> allMoves = new ArrayList<Move>();
        game.nextTurn();
        
        //get all possible Moves for the next Player
        for(int i = 0; i < 8; i++){
        	for(int j = 0; j < 8; j++){
        		ChessPiece chessPiece = game.getBoard().getChessPiece(i, j);
                if (chessPiece != null && chessPiece.getColor() == game.getWaitingForPlayer()) {
                	for(Move move : chessPiece.getLegalMoves(i, j, game, false)){
                		allMoves.add(move);
                	}
                }
        	}
        }
        // Try out each move
        for ( Move move : allMoves)  {
        	DummyPersistenceManager manager = new DummyPersistenceManager(game);
        	ChessPiece chessPiece = game.getBoard().getChessPiece(move.getX1(), move.getY1());
        	try{
        		game.moveInternal(chessPiece, move);
        	}catch(GameLogicException e){
        		continue;
        	}
        	// Calculate the value of the move
    		double score = (-1)*negaMax( depth - 1, game );
    		score = val - score;
    		manager.rollback();
    		if( score > max )
    			max = score;
        }
        return max;
    }
}
